// array indexes for the _signal arrays
const int OFF = -1; // not a valid array index, used as a flag
const int GREEN = 0;
const int YELLOW = 1;
const int RED = 2;
const int WALK = 3;

// arduino pin numbers for each LED: green, yellow, red
int north_south_signal[4] = {5, 6, 7, 11}; // mike 7-3
int east_west_signal[4] = {8, 9, 10, 12};  // mike 7-3

// interrupt number for the clock
int clock_interrupt = 0;
// interrupt number for the buttons
int button_press_interrupt = 1;

int car_sensor_input = 4;
int walk_button_input = 13;

// array indexes used for the signal_states array
const int NORTH_SOUTH = 0;
const int EAST_WEST = 1;

// this holds all the possible light combinations
// first column is the north/south signal, second is the east/west signal
int signal_states[8][2] = {
  {GREEN,RED},
  {YELLOW,RED},
  {RED,RED},
  {RED,GREEN},
  {RED,YELLOW},
  {RED,RED},  
  {RED,YELLOW},  
  {OFF,OFF}   
};

// start at the last state so it will loop back to the beginning
volatile int current_state = 5;
// timestamp in millis when the button was pressed. used for debouncing.
volatile int button_press_time = 0;

volatile int car_sensor_flag = 0;
volatile int walk_button_flag = 0;
volatile int walk_signal_state = 0;

// counts every tick since the system started
volatile long all_ticks = 0;
// counts the ticks since the last state change
volatile long state_ticks = 0;

// change intervals in seconds
int Min_Green = 1;
int Max_Green = 20;
int Yellow_Change = 5;
int Red_Clear = 2;

int Flash_Interval = 1;
int Flash_Mode_Start = 1000;
int Flash_Mode_Stop = 1005;

void tick() {
  all_ticks++;
  state_ticks++;
  
  if (all_ticks == Flash_Mode_Start) {
    init_flash_sequence();
  }  
  else if (all_ticks == Flash_Mode_Stop) {
    all_ticks = 0;
    init_signal_sequence();
  }
    
  switch (current_state) {
    case 0:
      if (car_sensor_flag == 1) {
        if (state_ticks >= Min_Green) {
          car_sensor_flag = 0;
          next_state();
        }
      } else {
        if (state_ticks >= Max_Green) next_state();
      }
      break;
    case 1:
    case 4:
      if (state_ticks >= Yellow_Change) next_state();
      break;    
    case 2:
    case 5:
      if (walk_button_flag == 1) {
        if (state_ticks < Red_Clear*10) {
          walk_signal_on();
        } else if (state_ticks < Red_Clear*20) {
          if (state_ticks % Flash_Interval == 0) {
            if (walk_signal_state == 1) {
              walk_signal_off();
            } else {
              walk_signal_on();
            }
          }
        } else {
          walk_signal_off();
          walk_button_flag = 0;
          next_state();
        }
      } else if (state_ticks > Red_Clear) {
        next_state();
      }
      break;
    case 3:
      if (state_ticks >= Min_Green) next_state();
      break;
    //case 4:
    //  if (state_ticks >= Yellow_Change) next_state();
    //  break;
    //case 5:
    //  if (state_ticks > Red_Clear) next_state();
    //  break;
    case 6:
      if (state_ticks >= Flash_Interval) next_state();
      break;
    case 7:
      if (state_ticks >= Flash_Interval) next_state();
      break;
  }
}


void walk_signal_on() {
  if (walk_signal_state == 0) {
    walk_signal_state = 1;
    change_walk_signal(north_south_signal, walk_signal_state);
    change_walk_signal(east_west_signal, walk_signal_state);
  }
}

void walk_signal_off() {
  if (walk_signal_state == 1) {
    walk_signal_state = 0;
    change_walk_signal(north_south_signal, walk_signal_state);
    change_walk_signal(east_west_signal, walk_signal_state);
  }
}

void change_light(int *signal, int current_light, int new_light) {
  if (current_light != new_light) {
    if (current_light != OFF) digitalWrite(signal[current_light], LOW);
    if (new_light != OFF) digitalWrite(signal[new_light], HIGH);
  }
}

void change_walk_signal(int *signal, int on_flag) {
  if (on_flag == 1) {
    digitalWrite(signal[WALK], HIGH);
  } else if (on_flag == 0 ) {
    digitalWrite(signal[WALK], LOW);
  }
}

// switches off all 3 lights, used to make sure we are in a consistent state
void signal_off(int *signal) {
  digitalWrite(signal[GREEN], LOW);
  digitalWrite(signal[YELLOW], LOW);
  digitalWrite(signal[RED], LOW);
  digitalWrite(signal[WALK], LOW);
}

void next_state() {
  // this will count from 0 to 5 and then loop around to 0 again (% = modulus operator)
  int new_state;
  if (current_state < 6) {
    new_state = (current_state+1) % 6;
  } else if (current_state == 6) {
    new_state = 7;
  } else if (current_state == 7) {
    new_state = 6;
  } 
  
  // figure out which light is currently lit on the N/S signal, and which one needs to be lit next
  int current_north_south = signal_states[current_state][NORTH_SOUTH];
  int new_north_south = signal_states[new_state][NORTH_SOUTH];

  // this is identical to the section above, just using the E/W signal
  int current_east_west = signal_states[current_state][EAST_WEST];
  int new_east_west = signal_states[new_state][EAST_WEST];

  // if they aren't equal, the light needs to change
  change_light(north_south_signal, current_north_south, new_north_south);
  change_light(east_west_signal, current_east_west, new_east_west);
  
  // after we've changed all the lights, update the current state
  current_state = new_state;
  state_ticks = 0;
}


// this is the interrupt service routine for the button
// uses a delay to prevent bouncing
void button_press_isr() {
  int debounce = 100;
  if (abs(millis()-button_press_time) < debounce) return;
  button_press_time = millis();

  if (digitalRead(car_sensor_input) == HIGH) car_sensor_flag = 1;
  if (digitalRead(walk_button_input) == HIGH) walk_button_flag = 1;
}


// any time we switch from one state to another, blank all the lights
// and inititalize variables so we start from a consistent state each time
void init_signal_sequence() {
  current_state = 5;
  signal_off(north_south_signal);
  signal_off(east_west_signal);
  change_light(north_south_signal, OFF, signal_states[current_state][NORTH_SOUTH]);
  change_light(east_west_signal, OFF, signal_states[current_state][EAST_WEST]);
  state_ticks = 0;
}
void init_flash_sequence() {
    signal_off(north_south_signal);
    signal_off(east_west_signal);
    current_state = 6;
    state_ticks = 0;
}


void setup() {
  noInterrupts();

  pinMode(north_south_signal[GREEN], OUTPUT);
  pinMode(north_south_signal[YELLOW], OUTPUT);
  pinMode(north_south_signal[RED], OUTPUT);
  pinMode(north_south_signal[WALK], OUTPUT);
  
  pinMode(east_west_signal[GREEN], OUTPUT);
  pinMode(east_west_signal[YELLOW], OUTPUT);
  pinMode(east_west_signal[RED], OUTPUT);
  pinMode(east_west_signal[WALK], OUTPUT);
  
  pinMode(car_sensor_input, INPUT);
  pinMode(walk_button_input, INPUT);
  
  attachInterrupt(clock_interrupt, tick, RISING);
  attachInterrupt(button_press_interrupt, button_press_isr, RISING);
  
  // force the lights to initialize
  init_signal_sequence();
  
  interrupts();
}


void loop() {

}





